<?php



class Snoopy
{

    /**** Public variables ****/

    /* user definable vars */

    public $host = "www.php.net"; // host name we are connecting to


    public $ip = ""; // 域名所对应的IP，可不指定


    public $port = 80; // port we are connecting to


    public $proxy_host = ""; // proxy host to use


    public $proxy_ip = ""; // 代理所对应的IP，可不指定


    public $proxy_port = ""; // proxy port to use


    public $proxy_user = ""; // proxy user to use


    public $proxy_pass = ""; // proxy password to use


    public $agent = 'Mozilla/5.0 (Windows; Windows NT 5.1; zh-CN; MSIE 6.0;)'; //Snoopy v1.2.4 agent we masquerade as


    public $referer = ""; // referer info to pass


    public $cookies = array(); // array of cookies to pass


    // $cookies["username"]="joe";
    public $rawheaders = array(); // array of raw headers to send


    // $rawheaders["Content-type"]="text/html";


    public $maxredirs = 5; // http redirection depth maximum. 0 = disallow


    public $lastredirectaddr = ""; // contains address of last redirected address


    public $offsiteok = true; // allows redirection off-site


    public $maxframes = 0; // frame content depth maximum. 0 = disallow


    public $expandlinks = true; // expand links to fully qualified URLs.


    // this only applies to fetchlinks()
    // submitlinks(), and submittext()
    public $passcookies = true; // pass set cookies back through redirects


    // NOTE: this currently does not respect
    // dates, domains or paths.


    public $user = ""; // user for http authentication


    public $pass = ""; // password for http authentication


    // http accept types
    public $accept = "text/html,application/xhtml+xml,application/xml;*/*;";

    public $results = ""; // where the content is put


    public $error = ""; // error messages sent here


    public $response_code = ""; // response code returned from server


    public $headers = array(); // headers returned from server sent here


    public $maxlength = 500000; // max return data length (body)


    public $read_timeout = 0; // timeout on read operations, in seconds


    // supported only since PHP 4 Beta 4
    // set to 0 to disallow timeouts
    public $timed_out = false; // if a read operation timed out


    public $status = 0; // http request status


    public $temp_dir = "/tmp"; // temporary directory that the webserver


    // has permission to write to.
    // under Windows, this should be C:\temp


    public $curl_path = "/usr/local/bin/curl";

    // Snoopy will use cURL for fetching
    // SSL content if a full system path to
    // the cURL binary is supplied here.
    // set to false if you do not have
    // cURL installed. See http://curl.haxx.se
    // for details on installing cURL.
    // Snoopy does *not* use the cURL
    // library functions built into php,
    // as these functions are not stable
    // as of this Snoopy release.


    //远程下载文件允许的保存的后缀，没有设置的将为html
    public $allow_suffix = array('html', 'htm', 'shtml', 'css', 'js', 'txt', 'jpg', 'jpeg', 'gif', 'bmp', 'png', 'gz', '7z', 'zip', 'rar', 'doc', 'xls', 'ppt', 'pdf', 'chm', 'fla', 'swf', 'mp3', 'wma', 'rm', 'rmvb', 'avi', 'wmv', '3gp');

    public $downfile_path = ''; //保存的文件夹，可以是绝对路径


    public $downfile_filetype = false; //重新设置文件名，abc123=随机字符20位，md5=md5算法32位，sha1=sha1算法40位，默认MD5


    public $downfile_selfpath = false; //上传文件创建的目录，留空则不设置


    public $getresults = true; //是否获取信息，如果设置否，将不获取任何信息


    /**** Private variables ****/

    protected $_maxlinelen = 4096; // max line length (headers)


    protected $_httpmethod = "GET"; // default http request method


    protected $_httpversion = "HTTP/1.0"; // default http request version


    protected $_submit_method = "POST"; // default submit method


    protected $_submit_type = "application/x-www-form-urlencoded"; // default submit type


    protected $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type


    protected $_redirectaddr = false; // will be set if page fetched is a redirect


    protected $_redirectdepth = 0; // increments on an http redirect


    protected $_frameurls = array(); // frame src urls


    protected $_framedepth = 0; // increments on frame depth


    protected $_isproxy = false; // set if using a proxy server


    protected $_fp_timeout = 30; // timeout for socket connection


    function __construct()
    {

    }

    public function get_header($URI, $HOST = null)
    {
        $tmpgetresults = $this->getresults;
        $this->getresults = false;
        $r = $this->fetch($URI, $HOST);
        $this->getresults = $tmpgetresults;
        return $r;
    }

    /*======================================================================*\
	Function:	download
	Purpose:	download the contents of a web page
				(and possibly other protocols in the
				future like ftp, nntp, gopher, etc.)
	Input:		$URI	the location of the page to fetch
	Input:		$downfile	指定文件路径
	Output:		$this->results	the output text from the fetch
\*======================================================================*/

    public function download($URI, $downfile)
    {
        $this->fetch($URI, null, $downfile);
    }

    /*======================================================================*\
	Function:	fetch
	Purpose:	fetch the contents of a web page
				(and possibly other protocols in the
				future like ftp, nntp, gopher, etc.)
	Input:		$URI	the location of the page to fetch
	Input:		$HOST   指定网址所对应IP（可省却PHP解析域名）
	Input:		$downfile
	Output:		$this->results	the output text from the fetch
\*======================================================================*/

    public function fetch($URI, $HOST = null, $downfile = null)
    {
        if ( $HOST && preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $HOST) )
        {
            $this->ip = $HOST;
        }

        if ( $downfile )
        {
            $this->_set_downloadfile($URI, $downfile);
        }

        //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS);
        $URI_PARTS = parse_url($URI);
        if ( ! empty($URI_PARTS["user"]) ) $this->user = $URI_PARTS["user"];
        if ( ! empty($URI_PARTS["pass"]) ) $this->pass = $URI_PARTS["pass"];
        if ( empty($URI_PARTS["query"]) ) $URI_PARTS["query"] = '';
        if ( empty($URI_PARTS["path"]) ) $URI_PARTS["path"] = '';

        switch ( strtolower($URI_PARTS["scheme"]) )
        {
            case "http" :
                $this->host = $URI_PARTS["host"];
                if ( ! empty($URI_PARTS["port"]) ) $this->port = $URI_PARTS["port"];
                $fp = null;
                if ( $this->_connect($fp) )
                {
                    if ( $this->_isproxy )
                    {
                        // using proxy, send entire URI
                        $this->_httprequest($URI, $fp, $URI, $this->_httpmethod, '', '', $downfile);
                    }
                    else
                    {
                        $path = $URI_PARTS["path"] . ($URI_PARTS["query"] ? "?" . $URI_PARTS["query"] : "");
                        // no proxy, send only the path
                        $this->_httprequest($path, $fp, $URI, $this->_httpmethod, '', '', $downfile);
                    }

                    $this->_disconnect($fp);

                    if ( $this->_redirectaddr )
                    {
                        /* url was redirected, check if we've hit the max depth */
                        if ( $this->maxredirs > $this->_redirectdepth )
                        {
                            // only follow redirect if it's on this site, or offsiteok is true
                            if ( preg_match("|^http://" . preg_quote($this->host) . "|i", $this->_redirectaddr) || $this->offsiteok )
                            {
                                /* follow the redirect */
                                $this->_redirectdepth ++;
                                $this->lastredirectaddr = $this->_redirectaddr;
                                $this->fetch($this->_redirectaddr);
                            }
                        }
                    }

                    if ( $this->_framedepth < $this->maxframes && count($this->_frameurls) > 0 )
                    {
                        $frameurls = $this->_frameurls;
                        $this->_frameurls = array();

                        while ( list ( , $frameurl ) = each($frameurls) )
                        {
                            if ( $this->_framedepth < $this->maxframes )
                            {
                                $this->fetch($frameurl);
                                $this->_framedepth ++;
                            }
                            else
                                break;
                        }
                    }
                }
                else
                {
                    return false;
                }
                return true;
                break;
            case "https" :
                if ( ! $this->curl_path ) return false;
                if ( function_exists("is_executable") ) if ( ! is_executable($this->curl_path) ) return false;
                $this->host = $URI_PARTS["host"];
                if ( ! empty($URI_PARTS["port"]) ) $this->port = $URI_PARTS["port"];
                if ( $this->_isproxy )
                {
                    // using proxy, send entire URI
                    $this->_httpsrequest($URI, $URI, $this->_httpmethod);
                }
                else
                {
                    $path = $URI_PARTS["path"] . ($URI_PARTS["query"] ? "?" . $URI_PARTS["query"] : "");
                    // no proxy, send only the path
                    $this->_httpsrequest($path, $URI, $this->_httpmethod);
                }

                if ( $this->_redirectaddr )
                {
                    /* url was redirected, check if we've hit the max depth */
                    if ( $this->maxredirs > $this->_redirectdepth )
                    {
                        // only follow redirect if it's on this site, or offsiteok is true
                        if ( preg_match("|^http://" . preg_quote($this->host) . "|i", $this->_redirectaddr) || $this->offsiteok )
                        {
                            /* follow the redirect */
                            $this->_redirectdepth ++;
                            $this->lastredirectaddr = $this->_redirectaddr;
                            $this->fetch($this->_redirectaddr);
                        }
                    }
                }

                if ( $this->_framedepth < $this->maxframes && count($this->_frameurls) > 0 )
                {
                    $frameurls = $this->_frameurls;
                    $this->_frameurls = array();

                    while ( list ( , $frameurl ) = each($frameurls) )
                    {
                        if ( $this->_framedepth < $this->maxframes )
                        {
                            $this->fetch($frameurl);
                            $this->_framedepth ++;
                        }
                        else
                            break;
                    }
                }
                return true;
                break;
            default :
                // not a valid protocol
                $this->error = 'Invalid protocol "' . $URI_PARTS["scheme"] . '"\n';
                return false;
                break;
        }
        return true;
    }

    /*======================================================================*\
	Function:	submit
	Purpose:	submit an http form
	Input:		$URI	the location to post the data
				$formvars	the formvars to use.
					format: $formvars["var"] = "val";
				$formfiles  an array of files to submit
					format: $formfiles["var"] = "/dir/filename.ext";
	Output:		$this->results	the text output from the post
\*======================================================================*/

    public function submit($URI, $formvars = "", $formfiles = "")
    {
        $postdata = $this->_prepare_post_body($formvars, $formfiles);

        $URI_PARTS = parse_url($URI);
        if ( ! empty($URI_PARTS["user"]) ) $this->user = $URI_PARTS["user"];
        if ( ! empty($URI_PARTS["pass"]) ) $this->pass = $URI_PARTS["pass"];
        if ( empty($URI_PARTS["query"]) ) $URI_PARTS["query"] = '';
        if ( empty($URI_PARTS["path"]) ) $URI_PARTS["path"] = '';

        switch ( strtolower($URI_PARTS["scheme"]) )
        {
            case "http" :
                $this->host = $URI_PARTS["host"];
                if ( ! empty($URI_PARTS["port"]) ) $this->port = $URI_PARTS["port"];
                $fp = null;
                if ( $this->_connect($fp) )
                {
                    if ( $this->_isproxy )
                    {
                        // using proxy, send entire URI
                        $this->_httprequest($URI, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);
                    }
                    else
                    {
                        $path = $URI_PARTS["path"] . ($URI_PARTS["query"] ? "?" . $URI_PARTS["query"] : "");
                        // no proxy, send only the path
                        $this->_httprequest($path, $fp, $URI, $this->_submit_method, $this->_submit_type, $postdata);
                    }

                    $this->_disconnect($fp);

                    if ( $this->_redirectaddr )
                    {
                        /* url was redirected, check if we've hit the max depth */
                        if ( $this->maxredirs > $this->_redirectdepth )
                        {
                            if ( ! preg_match("|^" . $URI_PARTS["scheme"] . "://|", $this->_redirectaddr) ) $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr, $URI_PARTS["scheme"] . "://" . $URI_PARTS["host"]);

                            // only follow redirect if it's on this site, or offsiteok is true
                            if ( preg_match("|^http://" . preg_quote($this->host) . "|i", $this->_redirectaddr) || $this->offsiteok )
                            {
                                /* follow the redirect */
                                $this->_redirectdepth ++;
                                $this->lastredirectaddr = $this->_redirectaddr;
                                if ( strpos($this->_redirectaddr, "?") > 0 ) $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get
                                else $this->submit($this->_redirectaddr, $formvars, $formfiles);
                            }
                        }
                    }

                    if ( $this->_framedepth < $this->maxframes && count($this->_frameurls) > 0 )
                    {
                        $frameurls = $this->_frameurls;
                        $this->_frameurls = array();

                        while ( list ( , $frameurl ) = each($frameurls) )
                        {
                            if ( $this->_framedepth < $this->maxframes )
                            {
                                $this->fetch($frameurl);
                                $this->_framedepth ++;
                            }
                            else
                                break;
                        }
                    }

                }
                else
                {
                    return false;
                }
                return true;
                break;
            case "https" :
                if ( ! $this->curl_path ) return false;
                if ( function_exists("is_executable") ) if ( ! is_executable($this->curl_path) ) return false;
                $this->host = $URI_PARTS["host"];
                if ( ! empty($URI_PARTS["port"]) ) $this->port = $URI_PARTS["port"];
                if ( $this->_isproxy )
                {
                    // using proxy, send entire URI
                    $this->_httpsrequest($URI, $URI, $this->_submit_method, $this->_submit_type, $postdata);
                }
                else
                {
                    $path = $URI_PARTS["path"] . ($URI_PARTS["query"] ? "?" . $URI_PARTS["query"] : "");
                    // no proxy, send only the path
                    $this->_httpsrequest($path, $URI, $this->_submit_method, $this->_submit_type, $postdata);
                }

                if ( $this->_redirectaddr )
                {
                    /* url was redirected, check if we've hit the max depth */
                    if ( $this->maxredirs > $this->_redirectdepth )
                    {
                        if ( ! preg_match("|^" . $URI_PARTS["scheme"] . "://|", $this->_redirectaddr) ) $this->_redirectaddr = $this->_expandlinks($this->_redirectaddr, $URI_PARTS["scheme"] . "://" . $URI_PARTS["host"]);

                        // only follow redirect if it's on this site, or offsiteok is true
                        if ( preg_match("|^http://" . preg_quote($this->host) . "|i", $this->_redirectaddr) || $this->offsiteok )
                        {
                            /* follow the redirect */
                            $this->_redirectdepth ++;
                            $this->lastredirectaddr = $this->_redirectaddr;
                            if ( strpos($this->_redirectaddr, "?") > 0 ) $this->fetch($this->_redirectaddr); // the redirect has changed the request method from post to get
                            else $this->submit($this->_redirectaddr, $formvars, $formfiles);
                        }
                    }
                }

                if ( $this->_framedepth < $this->maxframes && count($this->_frameurls) > 0 )
                {
                    $frameurls = $this->_frameurls;
                    $this->_frameurls = array();

                    while ( list ( , $frameurl ) = each($frameurls) )
                    {
                        if ( $this->_framedepth < $this->maxframes )
                        {
                            $this->fetch($frameurl);
                            $this->_framedepth ++;
                        }
                        else
                            break;
                    }
                }
                return true;
                break;

            default :
                // not a valid protocol
                $this->error = 'Invalid protocol "' . $URI_PARTS["scheme"] . '"\n';
                return false;
                break;
        }
        return true;
    }

    /*======================================================================*\
	Function:	fetchlinks
	Purpose:	fetch the links from a web page
	Input:		$URI	where you are fetching from
	Output:		$this->results	an array of the URLs
\*======================================================================*/

    public function fetchlinks($URI)
    {
        if ( $this->fetch($URI) )
        {
            if ( $this->lastredirectaddr ) $URI = $this->lastredirectaddr;
            if ( is_array($this->results) )
            {
                for( $x = 0; $x < count($this->results); $x ++ )
                    $this->results[$x] = $this->_striplinks($this->results[$x]);
            }
            else
                $this->results = $this->_striplinks($this->results);

            if ( $this->expandlinks ) $this->results = $this->_expandlinks($this->results, $URI);
            return true;
        }
        else
            return false;
    }

    /*======================================================================*\
	Function:	fetchform
	Purpose:	fetch the form elements from a web page
	Input:		$URI	where you are fetching from
	Output:		$this->results	the resulting html form
\*======================================================================*/

    public function fetchform($URI)
    {

        if ( $this->fetch($URI) )
        {

            if ( is_array($this->results) )
            {
                for( $x = 0; $x < count($this->results); $x ++ )
                    $this->results[$x] = $this->_stripform($this->results[$x]);
            }
            else
                $this->results = $this->_stripform($this->results);

            return true;
        }
        else
            return false;
    }

    /*======================================================================*\
	Function:	fetchtext
	Purpose:	fetch the text from a web page, stripping the links
	Input:		$URI	where you are fetching from
	Output:		$this->results	the text from the web page
\*======================================================================*/

    public function fetchtext($URI)
    {
        if ( $this->fetch($URI) )
        {
            if ( is_array($this->results) )
            {
                for( $x = 0; $x < count($this->results); $x ++ )
                    $this->results[$x] = $this->_striptext($this->results[$x]);
            }
            else
                $this->results = $this->_striptext($this->results);
            return true;
        }
        else
            return false;
    }

    /*======================================================================*\
	Function:	submitlinks
	Purpose:	grab links from a form submission
	Input:		$URI	where you are submitting from
	Output:		$this->results	an array of the links from the post
\*======================================================================*/

    public function submitlinks($URI, $formvars = "", $formfiles = "")
    {
        if ( $this->submit($URI, $formvars, $formfiles) )
        {
            if ( $this->lastredirectaddr ) $URI = $this->lastredirectaddr;
            if ( is_array($this->results) )
            {
                for( $x = 0; $x < count($this->results); $x ++ )
                {
                    $this->results[$x] = $this->_striplinks($this->results[$x]);
                    if ( $this->expandlinks ) $this->results[$x] = $this->_expandlinks($this->results[$x], $URI);
                }
            }
            else
            {
                $this->results = $this->_striplinks($this->results);
                if ( $this->expandlinks ) $this->results = $this->_expandlinks($this->results, $URI);
            }
            return true;
        }
        else
            return false;
    }

    /*======================================================================*\
	Function:	submittext
	Purpose:	grab text from a form submission
	Input:		$URI	where you are submitting from
	Output:		$this->results	the text from the web page
\*======================================================================*/

    public function submittext($URI, $formvars = "", $formfiles = "")
    {
        if ( $this->submit($URI, $formvars, $formfiles) )
        {
            if ( $this->lastredirectaddr ) $URI = $this->lastredirectaddr;
            if ( is_array($this->results) )
            {
                for( $x = 0; $x < count($this->results); $x ++ )
                {
                    $this->results[$x] = $this->_striptext($this->results[$x]);
                    if ( $this->expandlinks ) $this->results[$x] = $this->_expandlinks($this->results[$x], $URI);
                }
            }
            else
            {
                $this->results = $this->_striptext($this->results);
                if ( $this->expandlinks ) $this->results = $this->_expandlinks($this->results, $URI);
            }
            return true;
        }
        else
            return false;
    }

    /*======================================================================*\
	Function:	set_submit_multipart
	Purpose:	Set the form submission content type to
				multipart/form-data
\*======================================================================*/
    public function set_submit_multipart()
    {
        $this->_submit_type = "multipart/form-data";
    }

    /*======================================================================*\
	Function:	set_submit_normal
	Purpose:	Set the form submission content type to
				application/x-www-form-urlencoded
\*======================================================================*/
    public function set_submit_normal()
    {
        $this->_submit_type = "application/x-www-form-urlencoded";
    }

    /*======================================================================*\
	Private functions
\*======================================================================*/

    /*======================================================================*\
	Function:	_striplinks
	Purpose:	strip the hyperlinks from an html document
	Input:		$document	document to strip.
	Output:		$match		an array of the links
\*======================================================================*/

    protected function _striplinks($document)
    {
        preg_match_all('#<\s*a\s.*?href\s*=\s*'.            # find <a href=
                        '([\"\'])?' .                       # find single or double quote
                        '(?(1) (.*?)\\1 | ([^\s\>]+))' .    # if quote found, match up to next matching
                        " " .                               # quote, otherwise match up to next space
                        "#isx", $document, $links);

        // catenate the non-empty matches from the conditional subpattern


        while ( list ( $key, $val ) = each($links[2]) )
        {
            if ( ! empty($val) ) $match[] = $val;
        }

        while ( list ( $key, $val ) = each($links[3]) )
        {
            if ( ! empty($val) ) $match[] = $val;
        }

        // return the links
        return $match;
    }

    /*======================================================================*\
	Function:	_stripform
	Purpose:	strip the form elements from an html document
	Input:		$document	document to strip.
	Output:		$match		an array of the links
\*======================================================================*/

    protected function _stripform($document)
    {
        preg_match_all('#<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))#Usi', $document, $elements);

        // catenate the matches
        $match = implode("\r\n", $elements[0]);

        // return the links
        return $match;
    }

    /*======================================================================*\
	Function:	_striptext
	Purpose:	strip the text from an html document
	Input:		$document	document to strip.
	Output:		$text		the resulting text
\*======================================================================*/

    protected function _striptext($document)
    {

        // I didn't use preg eval (//e) since that is only available in PHP 4.0.
        // so, list your entities one by one here. I included some of the
        // more common ones.


        $search = array(
            "'<script[^>]*?>.*?</script>'si",  // strip out javascript
            "'<[\/\!]*?[^<>]*?>'si",           // strip out html tags
            "'([\r\n])[\s]+'",                 // strip out white space
            "'&(quot|#34|#034|#x22);'i",       // replace html entities
            "'&(amp|#38|#038|#x26);'i",        // added hexadecimal values
            "'&(lt|#60|#060|#x3c);'i",
            "'&(gt|#62|#062|#x3e);'i",
            "'&(nbsp|#160|#xa0);'i",
            "'&(iexcl|#161);'i",
            "'&(cent|#162);'i",
            "'&(pound|#163);'i",
            "'&(copy|#169);'i",
            "'&(reg|#174);'i",
            "'&(deg|#176);'i",
            "'&(#39|#039|#x27);'",
            "'&(euro|#8364);'i",  // europe
            "'&a(uml|UML);'",     // german
            "'&o(uml|UML);'",
            "'&u(uml|UML);'",
            "'&A(uml|UML);'",
            "'&O(uml|UML);'",
            "'&U(uml|UML);'",
            "'&szlig;'i");
        $replace = array("", "", "\\1", "\"", "&", "<", ">", " ", chr(161), chr(162), chr(163), chr(169), chr(174), chr(176), chr(39), chr(128), "ä", "ö", "ü", "Ä", "Ö", "Ü", "ß");

        $text = preg_replace($search, $replace, $document);

        return $text;
    }

    /*======================================================================*\
	Function:	_expandlinks
	Purpose:	expand each link into a fully qualified URL
	Input:		$links			the links to qualify
				$URI			the full URI to get the base from
	Output:		$expandedLinks	the expanded links
\*======================================================================*/

    protected function _expandlinks($links, $URI)
    {

        preg_match("/^[^\?]+/", $URI, $match);

        $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|", "", $match[0]);
        $match = preg_replace("|/$|", "", $match);
        $match_part = parse_url($match);
        $match_root = $match_part["scheme"] . "://" . $match_part["host"];

        $search = array("|^http://" . preg_quote($this->host) . "|i", "|^(\/)|i", "|^(?!http://)(?!mailto:)|i", "|/\./|", "|/[^\/]+/\.\./|");

        $replace = array("", $match_root . "/", $match . "/", "/", "/");

        $expandedLinks = preg_replace($search, $replace, $links);

        return $expandedLinks;
    }

    /*======================================================================*\
	Function:	_httprequest
	Purpose:	go get the http data from the server
	Input:		$url		the url to fetch
				$fp			the current open file pointer
				$URI		the full URI
				$body		body contents to send if any (POST)
	Output:
\*======================================================================*/

    protected function _httprequest($url, $fp, $URI, $http_method, $content_type = "", $body = "", $downfile = null)
    {
        $cookie_headers = '';
        if ( $this->passcookies && $this->_redirectaddr ) $this->setcookies();

        $URI_PARTS = parse_url($URI);
        if ( empty($url) ) $url = "/";
        $headers = $http_method . " " . $url . " " . $this->_httpversion . "\r\n";
        if ( ! empty($this->agent) ) $headers .= "User-Agent: " . $this->agent . "\r\n";
        if ( ! empty($this->host) && ! isset($this->rawheaders['Host']) )
        {
            $headers .= "Host: " . $this->host;
            if ( ! empty($this->port) && $this->port != '80' ) $headers .= ":" . $this->port;
            $headers .= "\r\n";
        }
        if ( ! empty($this->accept) ) $headers .= "Accept: " . $this->accept . "\r\n";

        if ( ! empty($this->referer) ) $headers .= "Referer: " . $this->referer . "\r\n";
        if ( ! empty($this->cookies) )
        {
            if ( ! is_array($this->cookies) ) $this->cookies = (array)$this->cookies;

            reset($this->cookies);
            if ( count($this->cookies) > 0 )
            {
                $cookie_headers .= 'Cookie: ';
                foreach ( $this->cookies as $cookieKey => $cookieVal )
                {
                    $cookie_headers .= $cookieKey . "=" . urlencode($cookieVal) . "; ";
                }
                $headers .= substr($cookie_headers, 0, - 2) . "\r\n";
            }
        }
        if ( ! empty($this->rawheaders) )
        {
            if ( ! is_array($this->rawheaders) ) $this->rawheaders = (array)$this->rawheaders;
            while ( list ( $headerKey, $headerVal ) = each($this->rawheaders) )
                $headers .= $headerKey . ": " . $headerVal . "\r\n";
        }
        if ( ! empty($content_type) )
        {
            $headers .= "Content-type: $content_type";
            if ( $content_type == "multipart/form-data" ) $headers .= "; boundary=" . $this->_mime_boundary;
            $headers .= "\r\n";
        }
        if ( ! empty($body) ) $headers .= "Content-length: " . strlen($body) . "\r\n";
        if ( ! empty($this->user) || ! empty($this->pass) ) $headers .= "Authorization: Basic " . base64_encode($this->user . ":" . $this->pass) . "\r\n";

        //add proxy auth headers
        if ( ! empty($this->proxy_user) ) $headers .= 'Proxy-Authorization: ' . 'Basic ' . base64_encode($this->proxy_user . ':' . $this->proxy_pass) . "\r\n";

        $headers .= "\r\n";

        // set the read timeout if needed
        if ( $this->read_timeout > 0 ) socket_set_timeout($fp, $this->read_timeout);
        $this->timed_out = false;

        fwrite($fp, $headers . $body, strlen($headers . $body));

        $this->_redirectaddr = false;
        unset($this->headers);

        while ( $currentHeader = fgets($fp, $this->_maxlinelen) )
        {
            if ( $this->read_timeout > 0 && $this->_check_timeout($fp) )
            {
                $this->status = - 100;
                return false;
            }

            if ( $currentHeader == "\r\n" ) break;

            // if a header begins with Location: or URI:, set the redirect
            if ( preg_match("/^(Location:|URI:)/i", $currentHeader) )
            {
                // get URL portion of the redirect
                preg_match("/^(Location:|URI:)[ ]+(.*)/i", chop($currentHeader), $matches);
                // look for :// in the Location header to see if hostname is included
                if ( ! preg_match("|\:\/\/|", $matches[2]) )
                {
                    // no host in the path, so prepend
                    $this->_redirectaddr = $URI_PARTS["scheme"] . "://" . $this->host . ":" . $this->port;
                    // eliminate double slash
                    if ( ! preg_match("|^/|", $matches[2]) ) $this->_redirectaddr .= "/" . $matches[2];
                    else $this->_redirectaddr .= $matches[2];
                }
                else
                    $this->_redirectaddr = $matches[2];
            }

            if ( preg_match("|^HTTP/|", $currentHeader) )
            {
                if ( preg_match("|^HTTP/[^\s]*\s(.*?)\s|", $currentHeader, $status) )
                {
                    $this->status = $status[1];
                }
                $this->response_code = $currentHeader;
            }

            if ( preg_match("|^Content-Range|i", $currentHeader) && $this->thefile )
            {
                preg_match("/^Content-Range: bytes ([0-9]+)\-([0-9]+)\/([0-9]+)/i", $currentHeader, $matches);
                if ( $matches[1] >= 0 )
                {
                    fseek($this->thefile, $matches[1]);
                }
            }

            $this->headers[] = $currentHeader;
        }

        if ( $this->getresults )
        {
            $results = '';
            do
            {
                $_data = fread($fp, $this->maxlength);
                if ( strlen($_data) == 0 )
                {
                    break;
                }
                if ( $this->thefile && $this->status >= 200 && $this->status < 299 )
                {
                    fwrite($this->thefile, $_data);
                }
                else
                {
                    $results .= $_data;
                }
            }
            while ( true );
        }

        if ( isset($this->thefile) && $this->thefile )
        {
            fclose($this->thefile);
        }

        if ( $this->read_timeout > 0 && $this->_check_timeout($fp) )
        {
            $this->status = - 100;
            return false;
        }

        // check if there is a a redirect meta tag


        if ( preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i", $results, $match) )

        {
            $this->_redirectaddr = $this->_expandlinks($match[1], $URI);
        }

        // have we hit our frame depth and is there frame src to fetch?
        if ( ($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i", $results, $match) )
        {
            $this->results[] = $results;
            for( $x = 0; $x < count($match[1]); $x ++ )
                $this->_frameurls[] = $this->_expandlinks($match[1][$x], $URI_PARTS["scheme"] . "://" . $this->host);
        }
        // have we already fetched framed content?
        elseif ( is_array($this->results) ) $this->results[] = $results;
        // no framed content
        else $this->results = $results;

        return true;
    }

    protected function _set_downloadfile($URI, $downfile)
    {
        //下载文件
        if ( ! $downfile ) return FALSE;
        if ( $downfile === TRUE )
        {
            $path = $this->$downfile_path !== false ? $this->$downfile_path : Core::config('core.upload.filepath');
            $selfpath = $this->downfile_selfpath !== false ? $this->downfile_selfpath : Core::config('core.upload.selfpath');
            if ( $selfpath )
            {
                $selfpath = rtrim(date($selfpath), '/') . '/';
            }
            if ( substr($path, 0, 1) == '/' || preg_match("/^[a-z]\:\//i", $path) )
            {
                $downfile = $path;
            }
            else
            {
                $downfile = DIR_WWWROOT . $path;
            }
            $downfile = rtrim($downfile, '/') . '/' . $selfpath;
            //创建文件夹
            $this->_createdir($downfile);

            $myuri = parse_url($URI);
            $myuri = explode('/', $myuri['path']);
            $urlname = $myuri[(count($myuri) - 1)];
            $urlname = explode('.', $urlname); //文件名称
            if ( count($urlname) > 1 )
            {
                $suffix = array_pop($urlname); //后缀
            }
            else
            {
                $suffix = 'html';
            }
            $urlname = implode('.', $urlname);
            $setname = $this->downfile_filetype ? $this->downfile_filetype : Core::config('core.upload.setname');
            if ( $setname == 'abc123' )
            {
                $downfile .= $this->_get_rand(20);
            }
            elseif ( $setname == 'md5' )
            {
                $downfile .= md5('abs234adsdfsdl32423fke_' . $URI);
            }
            elseif ( $setname == 'sha1' )
            {
                $downfile .= sha1('abs234adsdfsdl32423fke_' . $URI);
            }
            else
            {
                if ( empty($urlname) )
                {
                    $downfile .= $this->_get_rand(20);
                }
                else
                {
                    $downfile .= md5('abs234adsdfsdl32423fke_' . $URI);
                }
            }
            $downfile .= in_array(strtolower($suffix), $this->allow_suffix) ? '.' . $suffix : '.html';
        }
        else
        {
            if ( substr($downfile, 0, 1) != '/' && ! preg_match('/^[a-z]\:\//i', $downfile) )
            {
                $downfile = DIR_WWWROOT . $downfile;
            }
        }

        $this->downfile = $downfile;

        if ( ! file_exists($downfile) )
        {
            file_put_contents($downfile, '');
        }
        else
        {
            //设置断点续传
            if ( ! is_array($this->rawheaders) ) $this->rawheaders = (array)$this->rawheaders;
            $this->rawheaders['Range'] = 'bytes=' . filesize($downfile) . '-';
        }

        $this->thefile = @fopen($downfile, 'r+');
    }

    protected function _get_rand($len = 10)
    {
        $chr = '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z';
        $arr = explode(',', $chr);
        shuffle($arr);
        $str = implode('', $arr);
        $ran = rand(0, count($arr) - $len);
        $rand = substr($str, $ran, $len);
        return $rand;
    }

    protected function _createdir($dir)
    {
        if ( ! @is_dir($dir) )
        {
            $temp = explode('/', $dir);
            $cur_dir = "";
            for( $i = 0; $i < count($temp); $i ++ )
            {
                $cur_dir .= $temp[$i] . "/";
                if ( ! @is_dir($cur_dir) )
                {
                    @mkdir($cur_dir, 0777);
                }
            }
        }
    }

    /*======================================================================*\
	Function:	_httpsrequest
	Purpose:	go get the https data from the server using curl
	Input:		$url		the url to fetch
				$URI		the full URI
				$body		body contents to send if any (POST)
	Output:
\*======================================================================*/

    protected function _httpsrequest($url, $URI, $http_method, $content_type = "", $body = "")
    {
        if ( $this->passcookies && $this->_redirectaddr ) $this->setcookies();

        $headers = array();

        $URI_PARTS = parse_url($URI);
        if ( empty($url) ) $url = "/";
        // GET ... header not needed for curl
        //$headers[] = $http_method." ".$url." ".$this->_httpversion;
        if ( ! empty($this->agent) ) $headers[] = "User-Agent: " . $this->agent;
        if ( ! empty($this->host) ) if ( ! empty($this->port) ) $headers[] = "Host: " . $this->host . ":" . $this->port;
        else $headers[] = "Host: " . $this->host;
        if ( ! empty($this->accept) ) $headers[] = "Accept: " . $this->accept;
        if ( ! empty($this->referer) ) $headers[] = "Referer: " . $this->referer;
        if ( ! empty($this->cookies) )
        {
            if ( ! is_array($this->cookies) ) $this->cookies = (array)$this->cookies;

            reset($this->cookies);
            if ( count($this->cookies) > 0 )
            {
                $cookie_str = 'Cookie: ';
                foreach ( $this->cookies as $cookieKey => $cookieVal )
                {
                    $cookie_str .= $cookieKey . "=" . urlencode($cookieVal) . "; ";
                }
                $headers[] = substr($cookie_str, 0, - 2);
            }
        }
        if ( ! empty($this->rawheaders) )
        {
            if ( ! is_array($this->rawheaders) ) $this->rawheaders = (array)$this->rawheaders;
            while ( list ( $headerKey, $headerVal ) = each($this->rawheaders) )
                $headers[] = $headerKey . ": " . $headerVal;
        }
        if ( ! empty($content_type) )
        {
            if ( $content_type == "multipart/form-data" ) $headers[] = "Content-type: $content_type; boundary=" . $this->_mime_boundary;
            else $headers[] = "Content-type: $content_type";
        }
        if ( ! empty($body) ) $headers[] = "Content-length: " . strlen($body);
        if ( ! empty($this->user) || ! empty($this->pass) ) $headers[] = "Authorization: BASIC " . base64_encode($this->user . ":" . $this->pass);

        for( $curr_header = 0; $curr_header < count($headers); $curr_header ++ )
        {
            $safer_header = strtr($headers[$curr_header], "\"", " ");
            $cmdline_params .= " -H \"" . $safer_header . "\"";
        }

        if ( ! empty($body) ) $cmdline_params .= " -d \"$body\"";

        if ( $this->read_timeout > 0 ) $cmdline_params .= " -m " . $this->read_timeout;

        $headerfile = tempnam($temp_dir, "sno");

        exec($this->curl_path . " -k -D \"$headerfile\"" . $cmdline_params . " \"" . escapeshellcmd($URI) . "\"", $results, $return);

        if ( $return )
        {
            $this->error = "Error: cURL could not retrieve the document, error $return.";
            return false;
        }

        $results = implode("\r\n", $results);

        $result_headers = file("$headerfile");

        $this->_redirectaddr = false;
        unset($this->headers);

        for( $currentHeader = 0; $currentHeader < count($result_headers); $currentHeader ++ )
        {

            // if a header begins with Location: or URI:, set the redirect
            if ( preg_match("/^(Location: |URI: )/i", $result_headers[$currentHeader]) )
            {
                // get URL portion of the redirect
                preg_match("/^(Location: |URI:)\s+(.*)/", chop($result_headers[$currentHeader]), $matches);
                // look for :// in the Location header to see if hostname is included
                if ( ! preg_match("|\:\/\/|", $matches[2]) )
                {
                    // no host in the path, so prepend
                    $this->_redirectaddr = $URI_PARTS["scheme"] . "://" . $this->host . ":" . $this->port;
                    // eliminate double slash
                    if ( ! preg_match("|^/|", $matches[2]) ) $this->_redirectaddr .= "/" . $matches[2];
                    else $this->_redirectaddr .= $matches[2];
                }
                else
                    $this->_redirectaddr = $matches[2];
            }

            if ( preg_match("|^HTTP/|", $result_headers[$currentHeader]) ) $this->response_code = $result_headers[$currentHeader];

            $this->headers[] = $result_headers[$currentHeader];
        }

        // check if there is a a redirect meta tag


        if ( preg_match("'<meta[\s]*http-equiv[^>]*?content[\s]*=[\s]*[\"\']?\d+;[\s]*URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i", $results, $match) )
        {
            $this->_redirectaddr = $this->_expandlinks($match[1], $URI);
        }

        // have we hit our frame depth and is there frame src to fetch?
        if ( ($this->_framedepth < $this->maxframes) && preg_match_all("'<frame\s+.*src[\s]*=[\'\"]?([^\'\"\>]+)'i", $results, $match) )
        {
            $this->results[] = $results;
            for( $x = 0; $x < count($match[1]); $x ++ )
                $this->_frameurls[] = $this->_expandlinks($match[1][$x], $URI_PARTS["scheme"] . "://" . $this->host);
        }
        // have we already fetched framed content?
        elseif ( is_array($this->results) ) $this->results[] = $results;
        // no framed content
        else $this->results = $results;

        unlink("$headerfile");

        return true;
    }

    /*======================================================================*\
	Function:	setcookies()
	Purpose:	set cookies for a redirection
\*======================================================================*/

    public function setcookies()
    {
        for( $x = 0; $x < count($this->headers); $x ++ )
        {
            if ( preg_match('/^set-cookie:[\s]+([^=]+)=([^;]+)/i', $this->headers[$x], $match) ) $this->cookies[$match[1]] = urldecode($match[2]);
        }
    }

    /*======================================================================*\
	Function:	_check_timeout
	Purpose:	checks whether timeout has occurred
	Input:		$fp	file pointer
\*======================================================================*/

    protected function _check_timeout($fp)
    {
        if ( $this->read_timeout > 0 )
        {
            $fp_status = socket_get_status($fp);
            if ( $fp_status["timed_out"] )
            {
                $this->timed_out = true;
                return true;
            }
        }
        return false;
    }

    /*======================================================================*\
	Function:	_connect
	Purpose:	make a socket connection
	Input:		$fp	file pointer
\*======================================================================*/

    protected function _connect(&$fp)
    {
        if ( ! empty($this->proxy_host) && ! empty($this->proxy_port) )
        {
            $this->_isproxy = true;

            $host = $this->proxy_host;
            if ( $this->proxy_ip && preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->proxy_ip) ) $host = $this->proxy_ip;

            $port = $this->proxy_port;
        }
        else
        {
            $host = $this->host;
            //添加IP支持
            if ( $this->ip && preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->ip) ) $host = $this->ip;

            $port = $this->port;
        }

        $this->status = 0;

        $fp = fsockopen($host, $port, $errno, $errstr, $this->_fp_timeout);
        if ( $fp )
        {
            // socket connection succeeded


            return true;
        }
        else
        {
            // socket connection failed
            $this->status = $errno;
            switch ( $errno )
            {
                case - 3 :
                    $this->error = "socket creation failed (-3)";
                case - 4 :
                    $this->error = "dns lookup failure (-4)";
                case - 5 :
                    $this->error = "connection refused or timed out (-5)";
                default :
                    $this->error = "connection failed (" . $errno . ")";
            }
            return false;
        }
    }

    /*======================================================================*\
	Function:	_disconnect
	Purpose:	disconnect a socket connection
	Input:		$fp	file pointer
\*======================================================================*/

    protected function _disconnect($fp)
    {
        return (fclose($fp));
    }

    /*======================================================================*\
	Function:	_prepare_post_body
	Purpose:	Prepare post body according to encoding type
	Input:		$formvars  - form variables
				$formfiles - form upload files
	Output:		post body
\*======================================================================*/

    protected function _prepare_post_body($formvars, $formfiles)
    {
        settype($formvars, "array");
        settype($formfiles, "array");
        $postdata = '';

        if ( count($formvars) == 0 && count($formfiles) == 0 ) return;

        switch ( $this->_submit_type )
        {
            case "application/x-www-form-urlencoded" :
                reset($formvars);
                while ( list ( $key, $val ) = each($formvars) )
                {
                    if ( is_array($val) || is_object($val) )
                    {
                        while ( list ( $cur_key, $cur_val ) = each($val) )
                        {
                            $postdata .= urlencode($key) . "[]=" . urlencode($cur_val) . "&";
                        }
                    }
                    else
                        $postdata .= urlencode($key) . "=" . urlencode($val) . "&";
                }
                break;

            case "multipart/form-data" :
                $this->_mime_boundary = "Snoopy" . md5(uniqid(microtime()));

                reset($formvars);
                while ( list ( $key, $val ) = each($formvars) )
                {
                    if ( is_array($val) || is_object($val) )
                    {
                        while ( list ( $cur_key, $cur_val ) = each($val) )
                        {
                            $postdata .= "--" . $this->_mime_boundary . "\r\n";
                            $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n";
                            $postdata .= "$cur_val\r\n";
                        }
                    }
                    else
                    {
                        $postdata .= "--" . $this->_mime_boundary . "\r\n";
                        $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n";
                        $postdata .= "$val\r\n";
                    }
                }

                reset($formfiles);
                while ( list ( $field_name, $file_names ) = each($formfiles) )
                {
                    settype($file_names, "array");
                    while ( list ( , $file_name ) = each($file_names) )
                    {
                        if ( ! is_readable($file_name) ) continue;

                        $fp = fopen($file_name, "r");
                        $file_content = fread($fp, filesize($file_name));
                        fclose($fp);
                        $base_name = basename($file_name);

                        $postdata .= "--" . $this->_mime_boundary . "\r\n";
                        $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n";
                        $postdata .= "$file_content\r\n";
                    }
                }
                $postdata .= "--" . $this->_mime_boundary . "--\r\n";
                break;
        }

        return $postdata;
    }
}

?>
